Skip to content

feat: add optional Python gRPC server and proto scaffolding (#14) - #15

Open
antoinetoussaint-byte wants to merge 2 commits into
mainfrom
issue-14-feat-add-optional-python-grpc-server-and-proto
Open

feat: add optional Python gRPC server and proto scaffolding (#14)#15
antoinetoussaint-byte wants to merge 2 commits into
mainfrom
issue-14-feat-add-optional-python-grpc-server-and-proto

Conversation

@antoinetoussaint-byte

Copy link
Copy Markdown
Contributor

Closes #14.

Summary

  • Adds an opt-in, service-owned gRPC server to the generated FastAPI service. A grpc.aio listener runs in the same process as the FastAPI app (booted from and stopped with the FastAPI lifespan) on its own internal port, so protobuf becomes the canonical typed contract while FastAPI stays the ASGI/REST listener.
  • Everything is off by default: with no grpc-server block, the generated service and its layout are byte-for-byte unchanged (TestCreateRESTOnlyByDefault, and the existing REST create-to-run test still passes on the default path).
  • Reuses the existing Buf/proto tooling already used for dependency stubs, and the same endpoint/port machinery already used for REST, so the change stays small and consistent with the codebase.

What's included (issue's "Optional Python gRPC server" section)

  • grpc-server settings (enabled, proto) inline-embedded in Settings, plus a creation question defaulting to no.
  • Scaffolds code/proto/api.proto, Buf config, a grpc.aio server, a gRPC health service, a user-owned servicer seam (src/rpc/servicer.py, never overwritten) separate from generator-owned files, and a round-trip test — only when enabled.
  • Regenerates the Python protobuf + gRPC stubs from the proto during Sync (proto.NewBuf), deterministically (cached on the proto tree).
  • Creates/loads a Codefly gRPC endpoint in Builder and Runtime; wires native, container, and Kubernetes port mappings (CODEFLY_GRPC_PORT for native/container, a fixed containerPort/Service port for k8s).
  • Advertises Protocol_GRPC as an agent capability; graceful shutdown releases both listeners via the lifespan.

Deferred to follow-ups (not in this PR)

Issue #14 is an epic spanning three subsystems plus several explicitly-external prerequisites; landing it as one PR would not be one reviewable logical change. This PR implements the gRPC server foundation — the issue's "first required path" that the rest builds on. The following are not implemented here and should be tracked as follow-up PRs on top of this one:

  • Proto-derived MCP tools (mounting the MCP Python SDK, proto→JSON-Schema, tools/list / tools/call round-trip).
  • OpenAPI-derived MCP tools (operationId/x-mcp selection, dispatch through FastAPI dependencies).
  • MCP security (auth boundary for the mounted ASGI app, Host/Origin allowlist, CORS for MCP headers).
  • Envoy / ingress header and routing policy (the issue itself scopes these as integration prerequisites owned outside this repo).

Test plan

  • go build ./... and gofmt clean.
  • go test ./... passes except TestCreateToRunDocker, which requires a Docker daemon not available in this environment (fails identically on main — unrelated to this change).
  • New Go tests: settings parse, REST-only-by-default layout, gRPC-enabled create (2 endpoints + full scaffold), k8s manifests render the gRPC port only when opted in.
  • Python round-trip validated against the actual rendered templates: buf generate and grpc_tools.protoc both produce the stubs; the scaffolded grpc.aio server answers an Echo RPC and the health Check returns SERVING (tests/rpc/test_grpc.py, run via uv).
  • Full container/Kubernetes reachability of both listeners — exercised by the Docker create-to-run test, which needs a Docker daemon (unavailable here).

antoinetoussaint-byte and others added 2 commits August 25, 2026 16:42
Add an opt-in, service-owned grpc.aio listener to the generated FastAPI
service. It runs in the same process as the FastAPI app (booted from the
lifespan), on its own internal port, and stays entirely off by default so
existing REST-only services and their generated layout are unchanged.

- grpc-server settings (enabled/proto) + a creation question, disabled by
  default.
- Scaffold proto/api.proto, Buf config, a grpc.aio server, a gRPC health
  service, and a user-owned servicer seam when enabled.
- Regenerate the Python protobuf + gRPC stubs from the proto during Sync.
- Create/load a Codefly gRPC endpoint in Builder and Runtime; wire native,
  container, and Kubernetes port mappings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-safe (#14)

Addresses review findings on the optional gRPC server.

#1 (main.py): the grpc server was imported at module top level, so importing
src.main required the generated stubs. Runtime.Init runs GenerateOpenAPI, which
imports src.main *before* Sync generates those stubs (and they are git-ignored,
so a fresh clone has none) — Init failed with ModuleNotFoundError. The import
now lives inside the startup handler; OpenAPI generation never fires startup, so
the module imports cleanly without stubs.

#2 (proto location): the proto was scaffolded at code/proto/api.proto, but
core's LoadEndpoints re-derives the gRPC contract from standards.ProtoPath
(proto/api.proto at the service root) and the manifest stores no proto bytes.
The endpoint therefore reloaded with zero RPCs and dependent services could not
generate clients. The proto now lives at the service-root standard path (Buf and
grpcEndpoint read it there), and defaultProtoPath is bound to standards.ProtoPath
so it can never drift again.

#3 (server.py): add_insecure_port returns 0 instead of raising when a port can't
be bound, so the server would "start" listening on nothing while FastAPI stayed
healthy. It now raises, which also fails the pod's HTTP readiness probe via the
shared lifespan (covers the gRPC-readiness gap).

Tests: added a reload regression test asserting the gRPC endpoint keeps its RPCs
after LoadEndpoints (would have caught #2), and a check that src.rpc is never
imported at module top level (would have caught #1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add optional Python gRPC server and proto-derived MCP tools

1 participant